#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WEBGL
#define LOCAL_LOGIC_SERVICE
#endif
using CfgTable;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using NetProtocol.Enum;
using NetProtocol.POD;
using NetProtocol.Server;
using System;
using System.Collections.Generic;
using static NetProtocol.Server.SNetLevelLogic;

namespace IQIGame.Onigao.Logic
{
    public class SLevelLogicModule : Singleton<SLevelLogicModule>, SNetLevelLogic
    {
        private SLevelLogicModule() { }
        public static SNetLevelLogic NetCore => Instance;

        public CFSMModuleManager StateMachineManager { get; private set; }
        public BehaviacManager BehaviacManager { get; private set; }

        private List<Level> _levels = new List<Level>();
        private Dictionary<long, Level> _levelMap = new Dictionary<long, Level>();
        private Dictionary<long, LevelPlayer> _levelPlayers = new Dictionary<long, LevelPlayer>();
        private Dictionary<ISession, LevelPlayer> _sessionPlayers = new Dictionary<ISession, LevelPlayer>();

        public int LevelCount => _levels.Count;
#if LOCAL_LOGIC_SERVICE
        public int uniqueLevelLogicFrame
        {
            get
            {
                if (_levels.Count == 0)
                {
                    return -1;
                }
                return _levels[0].levelFrame;
            }
        }
#endif
        private void InitModuleManager()
        {
            if (StateMachineManager == null)
            {
                StateMachineManager = CFStateMachineManager.Instance.CreateManager(true);
                StateMachineManager.SetInterval(3);
            }

            if (BehaviacManager == null)
            {
                BehaviacManager = BehaviacGlobal.Instance.CreateManager(LgLevelConst.BehaviacUpdateInterval);
            }
        }

        private void ShutdownModuleManager()
        {
            if (StateMachineManager != null)
            {
                CFStateMachineManager.Instance.ReleaseManager(StateMachineManager);
                StateMachineManager = null;
            }

            if (BehaviacManager != null)
            {
                BehaviacGlobal.Instance.ReleaseManager(BehaviacManager);
                BehaviacManager = null;
            }
        }

#if UNITY_EDITOR
        /// <summary>
        /// 临时功能：存档当前唯一的单机关卡
        /// </summary>
        public LevelSavePOD GetTempLevelSaveData()
        {
            Level level = Instance._levels[0];
            if (!level.CanSaveLevel())
            {
                LogicLog.LogWarning("当前不能存档，请稍后再试。");
                return null;
            }
            LevelPlayer player = level.leaderPlayer;
            player.SaveCheckPoint();

            LevelSavePOD pod = level.GenerateSavePOD();
            return pod;
        }
#endif

        /// <summary>
        /// 关卡初始化逻辑
        /// </summary>
        public void LogicStart()
        {
            InitModuleManager();
            LogicDataSyncModule.Instance.Init();
        }

        /// <summary>
        /// 关卡Update
        /// </summary>
        public void LogicOnUpdate()
        {
            for (int i = 0; i < _levels.Count; i++)
            {
                _levels[i].LogicOnUpdate();
            }
            StateMachineManager.OnUpdate(LgLevelConst.deltaTime);
            BehaviacManager.OnUpdate(LgLevelConst.deltaTime);
        }

        public void LogicStop()
        {
            ShutdownModuleManager();
        }


        private void RemoveLevel(Level level)
        {
            _levelMap.Remove(level.id);
            _levels.Remove(level);
            ClassPool.Put(level);
        }

        public Level GetLevel(long id)
        {
            this._levelMap.TryGetValue(id, out var rLevel);
            return rLevel;
        }

        public Level GetLocalLevel()
        {
            if (_levels.Count > 1) throw new Exception("not local mode");
            if (_levels.Count == 1)
            {
                Level level = _levels[0];
                if (!level.isLocal) throw new Exception("not local mode");
                return level;
            }
            return null;
        }


        /// <summary>
        /// 编辑器模式专用方法
        /// </summary>
        /// <returns></returns>
        public Level EditorGetLevelByCid(int levelCid)
        {
#if UNITY_EDITOR

            foreach (var kv in _levelMap)
            {
                if (kv.Value.cfgLevel.Id == levelCid)
                {
                    return kv.Value;
                }
            }
#endif
            return null;
        }

        public LevelPlayer GetLevelPlayer(ISession session)
        {
            if (_sessionPlayers.TryGetValue(session, out LevelPlayer levelPlayer))
            {
                return levelPlayer;
            }
            return null;
        }

        /// <summary>
        /// 获取玩家的基础数据
        /// </summary>
        /// <param name="pid"></param>
        /// <returns></returns>
        public LogicPlayer GetPlayerData(long pid)
        {
            if (_levelPlayers.TryGetValue(pid, out LevelPlayer levelPlayer))
            {
                return levelPlayer.logicPlayer;
            }
            return null;
        }

        /// <summary>
        /// 预留接口，便于后期改造
        /// </summary>
        /// <param name="code"></param>
        /// <param name="msg"></param>
        private void NotifyError(int code, string msg = "")
        {
            if (code != ErrorCode.SUCCESS)
            {
                LogicLog.LogError($"Request Error, code: {code}, msg: {msg}");
            }
        }

        private bool ValidateLevel(CreateLevelPOD levelPOD)
        {
            CfgLevel cfgLevel = TableCenter.level.Get(levelPOD.cid);
            if (cfgLevel == null)
            {
                LogicLog.LogError($"找不到关卡：{levelPOD.cid}");
                return false;
            }
            return true;
        }

        /// <summary>
        /// 创建关卡，正式环境下由房间服调用
        /// </summary>
        /// <param name="levelPOD"></param>
        /// <param name="levelSavePOD"></param>
        /// <returns></returns>
        public bool CreateLevel(CreateLevelPOD levelPOD, LevelSavePOD levelSavePOD)
        {
            if (!ValidateLevel(levelPOD))
            {
                return false;
            }
            Level level = ClassPool.Get<Level>();
            level.Init(levelPOD, levelSavePOD);
            level.Build();
            _levelMap.Add(level.id, level); // 该抛异常抛异常
            _levels.Add(level);
            return true;
        }

        /// <summary>
        /// 玩家连接成功，客户端连接成功时逻辑服远端代码会调用该接口
        /// 该接口需负责通知客户端进入关卡
        /// </summary>
        public bool OnPlayerConnect(LogicPlayer logicPlayer, long levelId)
        {
            Level level = GetLevel(levelId);
            if (level == null)
            {
                LogicLog.LogError($"找不到关卡 {levelId}");
                return false;
            }
            LevelPlayer levelPlayer = level.InitPlayer(logicPlayer);
            if (levelPlayer == null)
            {
                LogicLog.LogError($"找不到玩家 {logicPlayer.id}");
                return false;
            }
            _levelPlayers.Add(levelPlayer.id, levelPlayer);
            _sessionPlayers.Add(logicPlayer.session, levelPlayer);
            level.EnterLevel(levelPlayer, logicPlayer.session); // 执行进入关卡

            // callback
            LogicHelper.OnPlayerEnterLevel?.Invoke(levelPlayer.id, levelId);
            return true;
        }

        public bool KickPlayerOut(long pid, bool shutdownIfNoPlayer = true)
        {
            if (_levelPlayers.TryGetValue(pid, out LevelPlayer levelPlayer))
            {
                LeaveLevel(levelPlayer, 0, 0, 0, shutdownIfNoPlayer);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 离开关卡
        /// </summary>
        /// <param name="player"></param>
        /// <param name="outRegionCid">退出关卡到达游戏模块（如主城）的地区cid</param>
        /// <param name="outPortalMcid">outRegionCid对应的地区内的传送点mcid</param>
        /// <param name="anotherLevelCid">退出后需要进入另一个关卡的cid</param>
        /// <param name="shutdownIfNoPlayer">如果关卡内已无玩家是否直接销毁</param>
        public void LeaveLevel(LevelPlayer player, int outRegionCid, int outPortalMcid, int anotherLevelCid, bool shutdownIfNoPlayer = true)
        {
            ShutdownBattle(player); // 关闭关卡创建的战斗

            var session = player.session;
            var playerId = player.id;
            Level level = player.level;
            var levelId = level.id;

            level.OnPlayerLeave(player); // 离开关卡 -- 会清除level引用
            // 彻底删除
            RemovePlayer(player);

            // 按理说，这里需要延迟删除 -- 外部可以主动触发立即删除
            if (shutdownIfNoPlayer && !level.HasPlayers())
            {
                ShutdownLevel(level.id);
            }
            // callback
            LogicHelper.OnPlayerLeaveLevel?.Invoke(playerId, levelId);
            // 发送通知给视图层
            NetCore.SC_NotifyLeave(session, outRegionCid, outPortalMcid, anotherLevelCid);
        }

        private void RemovePlayer(LevelPlayer player)
        {
            _levelPlayers.Remove(player.id);
            _sessionPlayers.Remove(player.session);
            ClassPool.Put(player);
        }

        /** 只能在玩家为空的情况下调用 */
        public bool ShutdownLevel(long levelId)
        {
            Level level = GetLevel(levelId);
            if (level == null)
            {
                return false;
            }
            if (level.HasPlayers())
            {
                throw new Exception("KickPlayerOut is required");
            }
            RemoveLevel(level);
            return true;
        }

        public void StartBattle(LevelPlayer player, CreateBattlePOD battlePOD, LogicPlayerPOD logicPlayerPOD)
        {
            player.battleId = battlePOD.id;

#if LOCAL_LOGIC_SERVICE
            //这里一定是本地战斗
            CBattleLogicModule.Instance.InitLocalBattle(battlePOD, logicPlayerPOD, OnBattleEnd);
#else
            SBattleLogicModule.Instance.CreateBattle(battlePOD, OnBattleEnd);
            SBattleLogicModule.Instance.OnPlayerConnect(player.logicPlayer, battlePOD.id);
#endif
        }

        private void OnBattleEnd(BattleEndData data)
        {
            if (_levelPlayers.TryGetValue(data.hostPlayerId, out LevelPlayer player))
            {
                // 同步关卡内英雄的hp和sp
                foreach (var statisticsPOD in data.statistics)
                {
                    var unitPOD = statisticsPOD.unit;
                    if (unitPOD.pid != player.id)
                    {
                        continue;
                    }
                    var levelHero = player.GetHero(unitPOD.configID);
                    if (levelHero != null)
                    {
                        int hp = unitPOD.attrs[(int)EAttributeType.Hp];
                        int sp = unitPOD.attrs[(int)EAttributeType.SP];
                        levelHero.SetHP(hp);
                        levelHero.SetSP(sp);
                    }
                }

                //退出战斗
                //按策划需求，迷宫内战斗结束不直接弹结算面板，而是直接退出，所以关闭战斗和结算通知可以同时进行

                //关闭战斗，调用该方法后客户端会切换到关卡
                ShutdownBattle(player);

                //结算通知
                var pod = new BattleSettlementPOD();
                pod.battleType = BattleType.Level;
                pod.resultType = SuccessTeamToBattleResult((BattleTeamId)data.successTeam);
                pod.statisticsPOD = data.GenStatisticPOD(data.hostPlayerId);
                pod.levelSettlementPOD = new BattleLevelSettlementPOD();
                NetCore.SC_NotifyBattleSettlement(player.session, pod);

                //关卡解除占用，触发服务
                player.level.OnBattleEnd(player, pod.resultType);

                //检查是否切换英雄以及队伍是否团灭
                player.CheckTeamHP();
            }
        }

        private BattleResultType SuccessTeamToBattleResult(BattleTeamId successTeam)
        {
            return successTeam switch
            {
                BattleTeamId.Red => BattleResultType.Success, // 红队是玩家
                BattleTeamId.Blue => BattleResultType.Failed, // 蓝队是怪物
                _ => BattleResultType.Draw // 平局
            };
        }

        public void ShutdownBattle(LevelPlayer player)
        {
            long battleId = player.battleId;
            if (battleId > 0)
            {
                player.battleId = 0; // 先置为0避免递归
                SBattleLogicModule.Instance.KickPlayerOut(player.id);
                SBattleLogicModule.Instance.ShutdownBattle(battleId);
            }
        }

#if UNITY_EDITOR
        public Level EditorGetLevelHasPlayer()
        {
            for (int i = 0; i < _levels.Count; i++)
            {
                Level level = _levels[i];
                if (level.players.Count > 0)
                {
                    return level;
                }
            }
            return null;
        }

        public LevelRegion EditorGetRegionHasPlayer()
        {
            for (int i = 0; i < _levels.Count; i++)
            {
                Level level = _levels[i];
                for (int j = 0; j < level.regions.Count; j++)
                {
                    LevelRegion region = level.regions[j];
                    if (region.players.Count > 0)
                    {
                        return region;
                    }
                }
            }
            return null;
        }
#endif

        #region CS_API 手动实现的客户端请求
        EnterRegionResult SNetLevelLogic.CS_EnterRegion(ISession session, int regionCid, int portalId)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_EnterRegionResult(ErrorCode.InvalidPlayer, 0, 0);
            }
            int errorCode = player.level.OnRequestEnterRegion(player, regionCid, portalId);
            return NetCore.SC_EnterRegionResult(errorCode, regionCid, portalId);
        }

        bool SNetLevelLogic.CS_Move(ISession session, float x, float y, float z)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                NotifyError(ErrorCode.InvalidPlayer, "player is not found.");
                return false;
            }

            player.level.Move(player, new Vector3Logic(x, y, z));
            return true;
        }

        ReadyToStartResult SNetLevelLogic.CS_ReadyToStart(ISession session)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_ReadyToStartResult(ErrorCode.InvalidPlayer);
            }
            return NetCore.SC_ReadyToStartResult(ErrorCode.SUCCESS).SetDelayCall(() =>
            {
                player.isReadyToStart = true;
                bool isAllReady = player.level.IsAllPlayersReady();
                if (isAllReady)
                {
                    player.level.Active();
                    NetCore.SC_NotifyStart_Broadcast(player.level.AllSessions);
                    player.region.TriggerEnter(player);
                }
            });
        }

        InteractResult SNetLevelLogic.CS_Interact(ISession session, int entityId)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                NotifyError(ErrorCode.InvalidPlayer, "player is not found.");
                return NetCore.SC_InteractResult(ErrorCode.InvalidPlayer, false);
            }
            bool hasTriggered = player.region.InteractWithEntity(player, entityId);
            return NetCore.SC_InteractResult(ErrorCode.SUCCESS, hasTriggered);
        }

        LeaveResult SNetLevelLogic.CS_Leave(ISession session, int outRegionCid, int outPortalMcid, int anotherLevelCid)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_LeaveResult(ErrorCode.InvalidPlayer);
            }
            return NetCore.SC_LeaveResult(ErrorCode.SUCCESS).SetDelayCall(() =>
            {
                //LeaveLevel会让玩家下线, 客户端收到下线消息会关闭session, SC_LeaveResult需要提前发送才能保证客户端正确收到消息
                LeaveLevel(player, outRegionCid, outPortalMcid, anotherLevelCid, true);
            });
        }

        JumpResult SNetLevelLogic.CS_Jump(ISession session, int entityId, float dirX, float dirZ)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_JumpResult(ErrorCode.InvalidPlayer);
            }
            int errorCode = player.level.HeroJump(player, entityId, dirX, dirZ);
            return NetCore.SC_JumpResult(errorCode);
        }

        bool SNetLevelLogic.CS_SwitchPhysicNavMode(ISession session, bool isPhysical, float x, float y, float z)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                NotifyError(ErrorCode.InvalidPlayer, "player is not found.");
                return false;
            }
            int errorCode = player.level.SwitchPhysicNavMode(player, isPhysical, x, y, z);
            return errorCode == ErrorCode.SUCCESS;
        }

        bool SNetLevelLogic.CS_TriggerInEntityState(ISession session, int entityId, int state)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                NotifyError(ErrorCode.InvalidPlayer, "player is not found.");
                return false;
            }
            int errorCode = player.level.OnTriggerInEntityState(player, entityId, state);
            return errorCode == ErrorCode.SUCCESS;
        }

        RepoStoryDialogSelectionResult SNetLevelLogic.CS_RepoStoryDialogSelection(ISession session, List<int> selectionIndexes)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_RepoStoryDialogSelectionResult(ErrorCode.InvalidPlayer);
            }
            int errorCode = player.level.OnRepoStoryDialogEnd(player, selectionIndexes);
            return NetCore.SC_RepoStoryDialogSelectionResult(errorCode);
        }

        GetMapEntitiesResult SNetLevelLogic.CS_GetMapEntities(ISession session, int regionCid)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_GetMapEntitiesResult(ErrorCode.InvalidPlayer, null);
            }
            return NetCore.SC_GetMapEntitiesResult(ErrorCode.SUCCESS, player.level.GetMapEntities(player, regionCid));
        }

        bool SNetLevelLogic.CS_TeleportToEntity(ISession session, int entityId)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                NotifyError(ErrorCode.InvalidPlayer, "player is not found.");
                return false;
            }
            var result = player.level.OnRequestTeleport(player, entityId);
            if (result == TeleportResult.NotActiveCheckPoint)
            {
                NotifyError(ErrorCode.InvalidPortal, "teleport failed, entity is not check point.");
                return false;
            }
            if (result == TeleportResult.NavigationError)
            {
                NotifyError(ErrorCode.NavigationError, "teleport failed, navigation error.");
                return false;
            }
            return true;
        }

        ConfirmItemSubmitResult SNetLevelLogic.CS_ConfirmItemSubmit(ISession session, List<long> itemIdList)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_ConfirmItemSubmitResult(ErrorCode.InvalidPlayer);
            }
            bool isNoError = player.level.OnConfirmItemSubmit(player, itemIdList, out bool isOpSuccess);
            return NetCore.SC_ConfirmItemSubmitResult(isNoError ? ErrorCode.SUCCESS : ErrorCode.InvalidStatus, isOpSuccess);
        }

        AcceptQuestResult SNetLevelLogic.CS_AcceptQuest(ISession session, int questCid)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_AcceptQuestResult(ErrorCode.InvalidPlayer);
            }
            bool isSucces = LogicQuestModule.Instance.AcceptQuest(player.logicPlayer, questCid);
            return NetCore.SC_AcceptQuestResult(isSucces ? ErrorCode.SUCCESS : ErrorCode.InvalidQuest);
        }

        RequestReviveResult SNetLevelLogic.CS_RequestRevive(ISession session)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_RequestReviveResult(ErrorCode.InvalidPlayer);
            }
            player.Revive();
            return NetCore.SC_RequestReviveResult(ErrorCode.SUCCESS);
        }

        MarkTrackingEntityResult SNetLevelLogic.CS_MarkTrackingEntity(ISession session, int entityId)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                return NetCore.SC_MarkTrackingEntityResult(ErrorCode.InvalidPlayer);
            }
            int code = player.region.ChangeManualTrackingEntity(player, entityId);
            return NetCore.SC_MarkTrackingEntityResult(code);
        }

        bool SNetLevelLogic.CS_PushEntityArrive(ISession session, int entityId, int x, int y, int z)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                NotifyError(ErrorCode.InvalidPlayer, "player is not found.");
                return false;
            }
            return player.region.EndPushEntity(player, entityId, x, y, z);
        }

        SwitchPhasingResult SNetLevelLogic.CS_SwitchPhasing(ISession session, GameScenePhasing phasing, int mainPhasingPosX, int mainPhasingPosY, int mainPhasingPosZ)
        {
            LevelPlayer player = GetLevelPlayer(session);
            if (player == null)
            {
                NotifyError(ErrorCode.InvalidPlayer, "player is not found.");
                return NetCore.SC_SwitchPhasingResult(ErrorCode.InvalidPlayer);
            }
            if (!player.region.SwitchPhasing(player, phasing, new Vector3Logic(mainPhasingPosX, mainPhasingPosY, mainPhasingPosZ)))
            {
                return NetCore.SC_SwitchPhasingResult(ErrorCode.InvalidStatus);
            }
            return NetCore.SC_SwitchPhasingResult(ErrorCode.SUCCESS);
        }
        #endregion CS_API
    }
}
